package cn.zwx.common.sentinel.aspect;

import cn.zwx.common.sentinel.properties.ZwxSentinePropreties;
import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.Tracer;
import com.alibaba.csp.sentinel.adapter.spring.webmvc.config.BaseWebMvcConfig;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Objects;

/**
 * @author zhangwenxue
 * 解决跟全局异常处理冲突问题
 */
@Aspect
@Component
@Slf4j
public class StatisticsExceptionCountAspect {

    @Resource
    @Lazy
    private BaseWebMvcConfig baseWebMvcConfig;

    @Resource
    private ZwxSentinePropreties propreties;


    @Around("@within(org.springframework.web.bind.annotation.RestController) || @within(org.springframework.stereotype.Controller)")
    public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
        Entry globalEntry = null;
        // 开启了全局限流 全局熔断才加载
        if (propreties.isGlobalEnable()){
            try {
                globalEntry = SphU.entry(propreties.getAppId());
            }catch (Exception e){
                cn.zwx.common.sentinel.handler.BlockExceptionHandler exceptionHandler = new cn.zwx.common.sentinel.handler.BlockExceptionHandler(propreties);
                return exceptionHandler.getVoidResponseEntity(request, e);
            }
        }
        try {
            // 执行目标方法
            return joinPoint.proceed();
        } catch (Throwable ex) {
            // 异常之后处理
            log.error("Exception in method: {}", joinPoint.getSignature().getName(), ex);
            Entry entry = traceException(ex);
            if (entry == null){
                throw ex;
            }else {
                BlockException blockError = entry.getBlockError();
                cn.zwx.common.sentinel.handler.BlockExceptionHandler handler = new cn.zwx.common.sentinel.handler.BlockExceptionHandler(propreties);
                Throwable exception = Objects.isNull(blockError)?ex:blockError;
                return handler.getVoidResponseEntity(request, exception);
            }

        }finally {
            if (globalEntry != null){
                globalEntry.exit();
            }
        }
    }
    /**
     * 统计异常
     * @param ex ex
     */
    private Entry traceException(Throwable ex) {
        Entry entry = getEntryInRequest();
        if (entry != null) {
            Tracer.traceEntry(ex, entry);
        }
        return entry;
    }
    protected Entry getEntryInRequest() {
        RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
        ServletRequestAttributes attributes = (ServletRequestAttributes)requestAttributes;
        HttpServletRequest request = attributes.getRequest();
        Object entryObject = request.getAttribute(baseWebMvcConfig.getRequestAttributeName());
        return entryObject == null ? null : (Entry)entryObject;
    }
}
